/*
* ============================================================================
*  Name        : sea_event.h
*  Part of     : Finite state machine utilities
*  Description : State machine event class definition
*  Version     : %version: %
*  
*
* ============================================================================
*/
/**
 * @file sea_event.h
 * @brief 
 * @ref sea::Event
 *
 * @ingroup StateMachineFramework
 */

#if !defined(__sea_event_h__ )
#define __sea_event_h__

#include "sea_platform.h"
#include "sea_trace.h"

namespace sea
{
    class StateMachine;

    /**
     * @class Event sea_event.h "sea_event.h"
     * @brief State Machine Event
     * @ingroup StateMachineFramework
     */
    class Event: public Object
    {
        friend class Manager;
        friend class StateMachine;
        friend class Timer;

    public:
        Event( Event* event );
        Event( StateMachine* handle, EventId event_id, bool sync_op = false );
        Event( StateMachine* handle, EventId event_id, void* event_data = 0, bool sync_op = false );
        Event( StateMachine* handle, EventId event_id, Object* event_data = 0, bool sync_op = false );
        ~Event( )
        {
            if ( m_data_is_object && m_data.m_object )
            {
                m_data.m_object->release_ref( );
            }
        }

        /**
         * Get interpreted event data
         *
         * @return T Event parameter pointer
         */
        template< typename T >
        T data( ) { return reinterpret_cast< T >( m_data.m_void ); }

        /**
         * Get interpreted event data
         *
         * @return T* Event parameter pointer
         */
        template< typename T >
        T* as( ) { return reinterpret_cast< T* >( m_data.m_void ); }

        /**
         * Retrieve owning state machine
         *
         * @return StateMachine*
         */
        StateMachine* get_state_machine( ) { return m_handle.get( ); }

        /**
         * Retrieve event id. Never had a need for that though. Action function
         * should be clueless of what event triggered it
         *
         * @return EventId
         */
        EventId get_event_id( ) { return m_event_id; }

        /**
         * Check if the event caused the action is SYNC. Never had a need for that though. Action function
         * should be clueless of the type of the event
         *
         * @return EventId
         */
        bool is_sync( ) { return m_sync; }

        /**
         * Get result of sync event. Event handler may prepare some data back to the requestor
         *
         * @return EventId
         */
        template< typename T >
        T result( ) 
        { 
            ASSERT_DEBUG( m_sync );
            return reinterpret_cast< T >( m_result );
        }

        /**
         * Store result in the event
         *
         * @param result 
         */
        template< typename T >
        void set_result( T result )
        {
            ASSERT_DEBUG( m_sync );
            m_result = reinterpret_cast< void* >( result );
        }

    private:

        void set_event_id( EventId id ){ m_event_id = id; }
        void set_stamp( int stamp ) { m_stamp = stamp; }
        int get_stamp( ) { return m_stamp; }
        void reset_stamp( );

        void wait( )
        {
            if( !m_sync )
            {
                return;
            }
            ASSERT_DEBUG( m_signal != NULL );
            m_signal->wait( );
        }

        void signal( )
        {
            if( !m_sync )
            {
                return;
            }
            ASSERT_DEBUG( m_signal != NULL );
            m_signal->signal( );
        }

    private:
        sea::ref< StateMachine > m_handle;      ///< State machine handle
        EventId m_event_id;                     ///< Event id
        union                                   ///< Event parameter
        {
            void* m_void;
            Object* m_object;
        } m_data;

        bool m_data_is_object;                  ///< Indicate that event data is an object ptr
        int m_stamp;                            ///< Stamp used for requestor identification

        sea::ref< OsSemaphore > m_signal;       ///< Allocated to sync events. DO NOT inherit Event from OsSemaphore for
                                                ///< performance reasons
        bool m_sync;                            ///< SYNC/ASYNC event
        void* m_result;                         ///< Placeholder for result for sync events
    };
}
#endif
